#include <stdio.h>
#include <assert.h>
#include "evaluate.h"
#include "backend.h"
#include "common.h"

void evaluate(struct evaluate_result *r, struct expression *ex, struct access_table *at, int context)
{
	/* TODO free used non-lvalues */

	struct evaluate_result n1, n2, n3;
	int context_notype;
	struct container *c;
	void *voidptr;

	context_notype = context & ~CONTEXT_G_TYPE;

	switch (ex->cate) {
	case ASSIGN:
		evaluate(r, ex->spec.assign.second, at, context_notype | CONTEXT_RVALUE);
		evaluate(&n1, ex->spec.assign.first, at, context_notype | CONTEXT_LVALUE);
		evaluate_assign(&n1, r);
		break;
	case FUNCCALL:
		evaluate_function(r, ex->spec.funccall.functionindex, ex->spec.funccall.argument_count, ex->spec.funccall.argument_table, at, context);
		break;
	case IDENTIFIER:	/* 1 of 2 possible l-value */
		evaluate_identifier(r, ex->spec.identifier.identifier, at, context);
		break;
	case ARRAYELEM:		/* 1 of 2 possible l-value */
		evaluate_array_element(r, ex->spec.array.identifier, ex->spec.array.index, at, context);
		break;
	case IMMEDIATE_INT:
		r->context = CONTEXT_IMMEDIATE | CONTEXT_RVALUE;
		r->type = validate_type_int();
		r->spec.integer.integer = ex->spec.immediate_int.value;
		break;
	case IMMEDIATE_BOOL:
		r->context = CONTEXT_IMMEDIATE | CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = ex->spec.immediate_bool.value ? true : false;
		break;

	case LOGICAL_NOT:
		evaluate(r, ex->spec.logical_not.expression, at, context_notype | CONTEXT_BOOLEAN);
		r->spec.boolean.boolean = !r->spec.boolean.boolean;
		r->context = CONTEXT_RVALUE | CONTEXT_BOOLEAN | CONTEXT_INTEGER;
		break;
	case LOGICAL_AND:
		evaluate(&n1, ex->spec.logical_and.first, at, context_notype | CONTEXT_BOOLEAN);
		evaluate(&n2, ex->spec.logical_and.second, at, context_notype | CONTEXT_BOOLEAN);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.boolean.boolean && n2.spec.boolean.boolean;
		break;
	case LOGICAL_OR:
		evaluate(&n1, ex->spec.logical_or.first, at, context_notype | CONTEXT_BOOLEAN);
		evaluate(&n2, ex->spec.logical_or.second, at, context_notype | CONTEXT_BOOLEAN);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.boolean.boolean || n2.spec.boolean.boolean;
		break;

	case COMPARE_EQ:
		evaluate(&n1, ex->spec.compare_eq.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_eq.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer == n2.spec.integer.integer;
		break;
	case COMPARE_NE:
		evaluate(&n1, ex->spec.compare_ne.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_ne.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer != n2.spec.integer.integer;
		break;
	case COMPARE_LT:
		evaluate(&n1, ex->spec.compare_lt.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_lt.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer < n2.spec.integer.integer;
		break;
	case COMPARE_LE:
		evaluate(&n1, ex->spec.compare_le.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_le.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer <= n2.spec.integer.integer;
		break;
	case COMPARE_GT:
		evaluate(&n1, ex->spec.compare_gt.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_gt.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer > n2.spec.integer.integer;
		break;
	case COMPARE_GE:
		evaluate(&n1, ex->spec.compare_ge.first, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n2, ex->spec.compare_ge.second, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context = CONTEXT_RVALUE;
		r->type = validate_type_bool();
		r->spec.boolean.boolean = n1.spec.integer.integer >= n2.spec.integer.integer;
		break;

	case NUMERIC_ADD:
		evaluate_numeric_binop(r, NUMERIC_ADD, ex->spec.numeric_add.first, ex->spec.numeric_add.second, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_SUB:
		evaluate_numeric_binop(r, NUMERIC_SUB, ex->spec.numeric_sub.first, ex->spec.numeric_sub.second, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_MUL:
		evaluate_numeric_binop(r, NUMERIC_MUL, ex->spec.numeric_mul.first, ex->spec.numeric_mul.second, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_DIV:
		evaluate_numeric_binop(r, NUMERIC_DIV, ex->spec.numeric_div.first, ex->spec.numeric_div.second, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_MOD:
		evaluate_numeric_binop(r, NUMERIC_MOD, ex->spec.numeric_mod.first, ex->spec.numeric_mod.second, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_LINC:
		evaluate_numeric_unop(r, NUMERIC_LINC, ex->spec.numeric_linc.expression, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_LDEC:
		evaluate_numeric_unop(r, NUMERIC_LDEC, ex->spec.numeric_ldec.expression, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_LSUB:
		evaluate_numeric_unop(r, NUMERIC_LSUB, ex->spec.numeric_lsub.expression, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_RINC:
		evaluate_numeric_unop(r, NUMERIC_RINC, ex->spec.numeric_rinc.expression, at, context & ~CONTEXT_G_TYPE);
		break;
	case NUMERIC_RDEC:
		evaluate_numeric_unop(r, NUMERIC_RDEC, ex->spec.numeric_rdec.expression, at, context & ~CONTEXT_G_TYPE);
		break;

	case CONDITIONAL:
		evaluate(&n1, ex->spec.conditional.condition, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_BOOLEAN);
		if (n1.spec.boolean.boolean) {
			evaluate(r, ex->spec.conditional.truevalue, at, context);
		} else {
			evaluate(r, ex->spec.conditional.falsevalue, at, context);
		}
		r->context &= ~CONTEXT_LVALUE;
		break;

	case ENUM_RANDOM:
		evaluate_enum_random(r, ex->spec.enum_random.enumcate, ex->spec.enum_random.enumindex, at, context & ~CONTEXT_G_TYPE);
		break;
	case ENUM_VALUE:
		evaluate(r, ex->spec.enum_value.expression, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		r->context &= ~CONTEXT_LVALUE;
		break;
	case ENUM_EXTRACT:
		evaluate_enum_extract(r, ex->spec.enum_extract.first, ex->spec.enum_extract.second, at, context);
		r->context &= ~CONTEXT_LVALUE;
		break;
	case ENUM_COMBINE:
		evaluate_enum_combine(r, ex->spec.enum_combine.component_count, ex->spec.enum_combine.component_table, at, context & ~CONTEXT_G_TYPE);
		r->context &= ~CONTEXT_LVALUE;
		break;

	case CONT_SIZE:
		evaluate(&n1, ex->spec.cont_size.expression, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		r->context = CONTEXT_RVALUE | CONTEXT_BOOLEAN | CONTEXT_INTEGER;
		r->type = validate_type_int();
		r->spec.integer.integer = container_size(n1.spec.container.container);
		break;
	case CONT_FILL:
		evaluate(&n1, ex->spec.cont_fill.expression, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		r->context = CONTEXT_NOVALUE;
		r->type = -1;
		container_fill(n1.spec.container.container);
		break;
	case CONT_SHUFFLE:
		evaluate(&n1, ex->spec.cont_shuffle.expression, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		r->context = CONTEXT_NOVALUE;
		r->type = -1;
		container_shuffle(n1.spec.container.container);
		break;
	case CONT_PEEK:
		evaluate(&n1, ex->spec.cont_peek.container, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		evaluate(&n2, ex->spec.cont_peek.position, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);

		c = n1.spec.container.container;
		r->type = c->type;
		switch (type_table[c->type].cate) {
			case BOOLEAN:
				r->spec.boolean.boolean = *(bool *)container_peek(c, n2.spec.integer.integer);
				break;
			case INTEGER:
				r->spec.integer.integer = *(int *)container_peek(c, n2.spec.integer.integer);
				break;
			case ENUM_BASIC:
				r->spec.enum_basic.elemindex = *(int *)container_peek(c, n2.spec.integer.integer);
				break;
			case ENUM_COMBO:
				validate_enum_combo_copy(c->type, &r->spec.enum_combo.elemindice, container_peek(c, n2.spec.integer.integer));
				break;
			default:
				assert(false);
				break;
		}
		break;
			
	case CONT_REMOVE:
		evaluate(&n1, ex->spec.cont_remove.container, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		evaluate(&n2, ex->spec.cont_remove.position, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate_convert(&n2, INTEGER);

		c = n1.spec.container.container;
		r->type = c->type;
		voidptr = container_remove(c, n2.spec.integer.integer);

		switch (type_table[c->type].cate) {
			case BOOLEAN:
				r->spec.boolean.boolean = *(bool *)voidptr;
				free(voidptr);
				break;
			case INTEGER:
				r->spec.integer.integer = *(int *)voidptr;
				free(voidptr);
				break;
			case ENUM_BASIC:
				r->spec.enum_basic.elemindex = *(int *)voidptr;
				free(voidptr);
				break;
			case ENUM_COMBO:
				r->spec.enum_combo.elemindice = voidptr;
				break;
			default:
				assert(false);
				break;
		}

		break;
	case CONT_INSERT:
		evaluate(&n1, ex->spec.cont_insert.container, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_CONT);
		evaluate(&n2, ex->spec.cont_insert.position, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);
		evaluate(&n3, ex->spec.cont_insert.element, at, context & ~CONTEXT_G_TYPE);	/* Type not checked */
		
		c = n1.spec.container.container;
			evaluate_convert(&n2, CONTEXT_INTEGER);
		switch (type_table[c->type].cate) {
			case INTEGER:
				container_insert(c, n2.spec.integer.integer, (void *)&n3.spec.integer.integer);
				break;
			case ENUM_BASIC:
				container_insert(c, n2.spec.integer.integer, (void *)&n3.spec.enum_basic.elemindex);
				break;
			case ENUM_COMBO:
				container_insert(c, n2.spec.integer.integer,(void *)&n3.spec.enum_combo.elemindice);
				break;
			default:
				assert(false);
				break;
		}
		
		break;
	default:
		assert(false);
		break;
	}

	evaluate_convert(r, context & CONTEXT_G_TYPE);

	if (context & CONTEXT_LVALUE)
		assert(r->context & CONTEXT_LVALUE);
}

void evaluate_assign(struct evaluate_result *dst, struct evaluate_result *src)
{
	assert((dst->context & CONTEXT_LVALUE) == CONTEXT_LVALUE);

	if (type_table[dst->type].cate == INTEGER) {
		evaluate_convert(src, CONTEXT_INTEGER);
		dst->spec.integer.integer = src->spec.integer.integer;
		*(int *)dst->location = src->spec.integer.integer;
	} else if (type_table[dst->type].cate == BOOLEAN) {
		evaluate_convert(src, CONTEXT_BOOLEAN);
		dst->spec.boolean.boolean = src->spec.boolean.boolean;
		*(bool *)dst->location = src->spec.boolean.boolean;
	} else if (type_table[dst->type].cate == ENUM_BASIC) {
		assert(dst->type == src->type);
		dst->spec.enum_basic.elemindex = src->spec.enum_basic.elemindex;
		*(int *)dst->location = src->spec.enum_basic.elemindex;
		
	} else if (type_table[dst->type].cate == ENUM_COMBO) {
		assert(dst->type == src->type);
		validate_enum_combo_copy_noalloc(dst->type, dst->spec.enum_combo.elemindice, src->spec.enum_combo.elemindice);
		validate_enum_combo_copy_noalloc(dst->type, dst->location, src->spec.enum_combo.elemindice);
	}
}

void evaluate_function(struct evaluate_result *r, int fidx, int argc, struct expression *argv, struct access_table *at, int context)
{
}

void evaluate_identifier(struct evaluate_result *r, char *id, struct access_table *at, int context)
{
	void *loc;
	int type;

	if (validate_enum_basic_element(&type, &r->spec.enum_basic.elemindex, id) == 0) {
		r->type = type;
		r->context = CONTEXT_RVALUE;
		return;
	}

	access_variable_content(&loc, &type, id, at, context & CONTEXT_G_ENV);
	evaluate_location(r, loc, type);
	assert((r->context & CONTEXT_LVALUE) == CONTEXT_LVALUE || (r->context & CONTEXT_CONT) == CONTEXT_CONT);
}

void evaluate_array_element(struct evaluate_result *r, char *id, struct expression *index, struct access_table *at, int context)
{
	int nindex, elemtype;
	void *base;
	struct evaluate_result vr, zr;

	evaluate_identifier(&vr, id, at, context & CONTEXT_G_ENV);
	assert((vr.context & CONTEXT_ARRAY) == CONTEXT_ARRAY);

	base = vr.spec.array.array;

	if (type_table[vr.type].cate == ARRAY_INT) {
		evaluate(&zr, index, at, (context & CONTEXT_G_ENV) | CONTEXT_INTEGER);
		nindex = zr.spec.integer.integer;
		elemtype = type_table[vr.type].spec.array_int.elemtype;
		base += nindex * validate_type_size(elemtype);
	} else if (type_table[vr.type].cate == ARRAY_ENUM_BASIC || type_table[vr.type].cate == ARRAY_ENUM_COMBO) {
		evaluate(&zr, index, at, (context & CONTEXT_G_ENV) | CONTEXT_INDEX);
		nindex = zr.spec.integer.integer;
		elemtype = type_table[vr.type].cate == ARRAY_ENUM_BASIC ? type_table[vr.type].spec.array_enum_basic.elemtype :
									  type_table[vr.type].spec.array_enum_combo.elemtype;
		base += nindex * validate_type_size(elemtype);
	} else {
		assert(false);
	}

	evaluate_location(r, base, elemtype);
	assert((r->context & CONTEXT_LVALUE) == CONTEXT_LVALUE || (r->context & CONTEXT_CONT) == CONTEXT_CONT);
}

void evaluate_location(struct evaluate_result *r, void *loc, int type)
{
	struct type *t;

	r->type = type;
	t = &type_table[type];
	switch (t->cate) {
	case INTEGER:
		r->context = CONTEXT_LVALUE | CONTEXT_RVALUE;
		r->spec.integer.integer = *(int *)loc;
		r->location = loc;
		break;
	case BOOLEAN:
		r->context = CONTEXT_LVALUE | CONTEXT_RVALUE;
		r->spec.boolean.boolean = *(bool *)loc;
		r->location = loc;
		break;
	case ENUM_BASIC:
		r->context = CONTEXT_LVALUE | CONTEXT_RVALUE;
		r->spec.enum_basic.elemindex = *(bool *)loc;
		r->location = loc;
		break;
	case ENUM_COMBO:
		r->context = CONTEXT_LVALUE | CONTEXT_RVALUE;
		validate_enum_combo_copy(type, &r->spec.enum_combo.elemindice, loc);
		r->location = loc;
		break;
	case ARRAY_INT:
	case ARRAY_ENUM_BASIC:
	case ARRAY_ENUM_COMBO:
		r->context = CONTEXT_ARRAY;
		r->spec.array.array = loc;
		break;
	case CONTAINER:
		r->context = CONTEXT_CONT;
		r->spec.container.container = loc;
		break;
	default:
		assert(false);
	}

	assert((r->context & CONTEXT_LVALUE) == CONTEXT_LVALUE || (r->context & CONTEXT_CONT) == CONTEXT_CONT || (r->context & CONTEXT_ARRAY) == CONTEXT_ARRAY);
}

void evaluate_numeric_binop(struct evaluate_result *r, enum expression_cate cate, struct expression *left, struct expression *right, struct access_table *at, int context)
{
	struct evaluate_result l_evaluate;
	struct evaluate_result r_evaluate;
	struct type *l_type;
	struct type *r_type;

	evaluate(&l_evaluate, left, at, context & ~CONTEXT_G_TYPE);
	evaluate(&r_evaluate, right, at, context & ~CONTEXT_G_TYPE);
	l_type = &type_table[l_evaluate.type];
	r_type = &type_table[r_evaluate.type];

	if (l_type->cate == ENUM_BASIC && r_type->cate == INTEGER) {

		/* ENUM_BASIC +/- INTEGER */

		int result_index;
		int max_index = enum_basic_table[l_type->spec.enum_basic.enumindex].count;

		switch (cate) {
		case NUMERIC_ADD:
			result_index = l_evaluate.spec.enum_basic.elemindex +
				       r_evaluate.spec.integer.integer;
			break;
		case NUMERIC_SUB:
			result_index = l_evaluate.spec.enum_basic.elemindex -
				       r_evaluate.spec.integer.integer;
			break;
		default:
			assert(false);
			break;
		}
		
		r->type = l_evaluate.type;
		r->spec.enum_basic.elemindex = result_index % max_index;
		r->context = CONTEXT_RVALUE | CONTEXT_INTEGER | CONTEXT_BOOLEAN | CONTEXT_ENUM;

	} else if ((l_type->cate == INTEGER || l_type->cate == BOOLEAN) &&
		   (r_type->cate == INTEGER || r_type->cate == BOOLEAN)) {

		/* INTEGER/BOOLEAN + INTEGER/BOOLEAN */ 

		int left_value, right_value, result_value;
	
		evaluate_convert(&l_evaluate, CONTEXT_INTEGER);
		evaluate_convert(&r_evaluate, CONTEXT_INTEGER);

		left_value = l_evaluate.spec.integer.integer;
		right_value = r_evaluate.spec.integer.integer;

		switch (cate) {
		case NUMERIC_ADD:
			result_value = left_value + right_value;
			break;
		case NUMERIC_SUB:
			result_value = left_value - right_value;
			break;
		case NUMERIC_MUL:
			result_value = left_value * right_value;
			break;
		case NUMERIC_DIV:
			T_ERROR_NONZERO(right_value);
			result_value = left_value / right_value;
			break;
		case NUMERIC_MOD:
			T_ERROR_NONZERO(right_value);
			result_value = left_value % right_value;
			break;
		default:
			assert(false);
			break;
		}

		r->type = validate_type_int();
		r->spec.integer.integer = result_value;
		r->context = CONTEXT_RVALUE | CONTEXT_INTEGER | CONTEXT_BOOLEAN;

	} else {
		assert(false);
	}

}

void evaluate_numeric_unop(struct evaluate_result *r, enum expression_cate cate, struct expression *operand, struct access_table *at, int context)
{
	struct evaluate_result operand_evaluate;
	struct evaluate_result postop_evaluate;
	
	struct type *operand_type;
	
	evaluate(&operand_evaluate, operand, at, context | CONTEXT_LVALUE);
	evaluate(&postop_evaluate, operand, at, context & ~CONTEXT_LVALUE);

	operand_type = &type_table[operand_evaluate.type];
	
	if (operand_type->cate == INTEGER) {

		/* INTEGER ++ */

		int preop_value =  operand_evaluate.spec.integer.integer;
		int postop_value;
		int evaluate_value;
		
		switch (cate) {
		case NUMERIC_LINC:
			postop_value = preop_value + 1;
			evaluate_value = postop_value;
			break;

		case NUMERIC_LDEC:
			postop_value = preop_value - 1;
			evaluate_value = postop_value;
			break;

		case NUMERIC_LSUB:
			postop_value = -preop_value;
			evaluate_value = postop_value;
			break;

		case NUMERIC_RINC:
			postop_value = preop_value + 1;
			evaluate_value = preop_value;
			break;

		case NUMERIC_RDEC:
			postop_value = preop_value - 1;
			evaluate_value = preop_value;
			break;
				
		default:
			assert(false);
		}
		
		r->type = validate_type_int();
		r->spec.integer.integer = evaluate_value;
		postop_evaluate.spec.integer.integer = postop_value;
		
	} else if (operand_type->cate == ENUM_BASIC) {

		/* ENUM_BASIC ++ */
		
		int preop_index = operand_evaluate.spec.enum_basic.elemindex;
		int postop_index;
		int evaluate_index;
		int max_index = enum_basic_table[operand_type->spec.enum_basic.enumindex].count;
		
		switch (cate) {
				
		case NUMERIC_LINC:
			postop_index = (preop_index + 1) % max_index;
			evaluate_index = postop_index;
			break;

		case NUMERIC_LDEC:
			postop_index = (preop_index - 1) % max_index;
			evaluate_index = postop_index;
			break;

		case NUMERIC_RINC:
			postop_index = (preop_index + 1) % max_index;
			evaluate_index = preop_index;
			break;

		case NUMERIC_RDEC:
			postop_index = (preop_index - 1) % max_index;
			evaluate_index = preop_index;
			break;

		default:
			assert(false);
		}
		
		r->type = operand_evaluate.type;
		r->spec.enum_basic.elemindex = evaluate_index;
		postop_evaluate.spec.enum_basic.elemindex = postop_index;
	} else {
		assert(false);
	}

	evaluate_assign(&operand_evaluate, &postop_evaluate);
	return;
}

void evaluate_enum_random(struct evaluate_result *r, enum type_cate enumcate, int enumidx, struct access_table *at, int context)
{
	if( enumcate == ENUM_BASIC) {
		int max_index = enum_basic_table[enumidx].count;
		int rand_index = rand() % max_index;
		r->type = validate_type_enum_basic_index(enumidx);
		r->spec.enum_basic.elemindex = rand_index;
		return;
	} else if (enumcate == ENUM_COMBO) {
		int i;
		struct enum_combo *combo = &enum_combo_table[enumidx];
		int *rand_indice = (int *)malloc(combo->compo_count * sizeof(int));
		T_ERROR_MALLOC(rand_indice);
		
		for (i = 0; i < combo->compo_count; i ++) {
			rand_indice[i] = rand() % enum_basic_table[combo->components[i]].count;
		}
		r->type = validate_type_enum_combo_index(enumidx);
		r->spec.enum_combo.elemindice = rand_indice;
		return;
	} else {
		assert(false);
	}
}

void evaluate_enum_extract(struct evaluate_result *r, struct expression *combo, struct expression *index, struct access_table *at, int context)
{
	struct evaluate_result combo_evaluate;
	struct evaluate_result index_evaluate;
	int index_value;
	
	evaluate(&combo_evaluate, combo, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_ENUM);
	evaluate(&index_evaluate, index, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INDEX);
	
	index_value = index_evaluate.spec.integer.integer;

	r->type = validate_type_enum_basic_index(enum_combo_table[type_table[combo_evaluate.type].spec.enum_combo.enumindex].components[index_value]);
	r->spec.enum_basic.elemindex = combo_evaluate.spec.enum_combo.elemindice[index_value];
}

void evaluate_enum_combine(struct evaluate_result *r, int count, struct expression *table, struct access_table *at, int context)
{
	int i;
	int *component_indices = (int *) malloc(sizeof(int) * count);
	int *element_indices = (int *) malloc(sizeof(int) * count);
	
	for (i = 0; i < count; i ++) {
		struct evaluate_result enum_evaluate;
		evaluate(&enum_evaluate, &table[i], at, context | CONTEXT_ENUM);
		
		component_indices[i] = type_table[enum_evaluate.type].spec.enum_basic.enumindex;
		element_indices[i] = enum_evaluate.spec.enum_basic.elemindex;
	}
	
	r->type = validate_type_enum_combo_compoindice(count, component_indices);
	r->spec.enum_combo.elemindice = element_indices;
	return;
}

int evaluate_int(struct expression *ex, struct access_table *at, int context)
{
	struct evaluate_result vr;

	evaluate(&vr, ex, at, (context & ~CONTEXT_G_TYPE) | CONTEXT_INTEGER);

	return vr.spec.integer.integer;
}

void evaluate_convert(struct evaluate_result *r, int context) 
{
	struct type *r_type;

	r_type = &type_table[r->type];

	switch (context & CONTEXT_G_TYPE) {
	case CONTEXT_INTEGER:
		switch (r_type->cate) {
		case INTEGER:
			break;
		case BOOLEAN:
			r->spec.integer.integer = r->spec.boolean.boolean ? 1 : 0;
			r->context &= ~CONTEXT_LVALUE;
			break;
		case ENUM_BASIC:
			r->spec.integer.integer = enum_basic_table[r_type->spec.array_enum_basic.enumindex].elements[r->spec.enum_basic.elemindex].value;
printf("%d\n", r->spec.integer.integer);
			r->context &= ~CONTEXT_LVALUE;
			break;
		case ENUM_COMBO:
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
		case CONTAINER:
		default:
			assert(false);
		}

		r->type = validate_type_int();

		break;

	case CONTEXT_INDEX:
		switch (r_type->cate) {
		case INTEGER:
			break;
		case ENUM_BASIC:
			r->spec.integer.integer = r->spec.enum_basic.elemindex;
			r->context &= ~CONTEXT_LVALUE;
			break;
		case ENUM_COMBO:
			r->spec.integer.integer = validate_enum_combo_index(r->type, r->spec.enum_combo.elemindice);
			r->context &= ~CONTEXT_LVALUE;
			break;
		case BOOLEAN:
			r->spec.integer.integer = r->spec.boolean.boolean ? 1 : 0;
			break;
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
		case CONTAINER:
		default:
			assert(false);
		}

		r->type = validate_type_int();

		break;

	case CONTEXT_BOOLEAN:
		switch (r_type->cate) {
		case INTEGER:
			r->spec.boolean.boolean = r->spec.integer.integer != 0;
			r->context &= ~CONTEXT_LVALUE;
			break;
		case BOOLEAN:
			break;
		case ENUM_BASIC:
		case ENUM_COMBO:
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
		case CONTAINER:
		default:
			assert(false);
		}

		r->type = validate_type_bool();

		break;

	case CONTEXT_ENUM:
		switch (r_type->cate) {
		case ENUM_BASIC:
		case ENUM_COMBO:
			break;
		case CONTAINER:
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
		case INTEGER:
		case BOOLEAN:
		default:
			assert(false);
		}

		break;

	case CONTEXT_CONT:
		switch (r_type->cate) {
		case CONTAINER:
			break;
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
		case INTEGER:
		case BOOLEAN:
		case ENUM_BASIC:
		case ENUM_COMBO:
		default:
			assert(false);
		}

		break;

	case CONTEXT_ARRAY:
		switch (r_type->cate) {
		case ARRAY_INT:
		case ARRAY_ENUM_BASIC:
		case ARRAY_ENUM_COMBO:
			break;
		case INTEGER:
		case BOOLEAN:
		case ENUM_BASIC:
		case ENUM_COMBO:
		case CONTAINER:
		default:
			assert(false);
		}

		break;

	case CONTEXT_NORMAL:
		break;

	default:
		assert(false);
	}
}

